#include <sys/time.h>

#include "./utils/core_itcp.h"

#define OS_LINUX
//#define OS_RTTHREAD

static void _core_aiot_itcp_exec_inc(core_itcp_handle_t *itcp_handle)
{
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    itcp_handle->exec_count++;
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);
}

static void _core_aiot_itcp_exec_dec(core_itcp_handle_t *itcp_handle)
{
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    itcp_handle->exec_count--;
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);
}

static void _core_itcp_auth_recv_handler(void *handle, const aiot_itcp_recv_t *packet, void *userdata)
{
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;
    core_itcp_response_t *response = (core_itcp_response_t *)userdata;

    switch (packet->type) {
        case AIOT_ITCPRECV_STATUS_CODE: {
            response->code = packet->data.status_code.code;
        }
        break;
        case AIOT_ITCPRECV_HEADER:
        break;
        case AIOT_ITCPRECV_BODY: {
            uint8_t *content = itcp_handle->sysdep->core_sysdep_malloc(response->content_len + packet->data.body.len, CORE_ITCP_MODULE_NAME);
            if (content == NULL) {
                return;
            }
            memset(content, 0x00, response->content_len + packet->data.body.len);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                itcp_handle->sysdep->core_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {
        }
        break;
    }
}

static void _core_itcp_recv_handler(void *handle, const aiot_itcp_recv_t *packet, void *userdata)
{
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;
    core_itcp_response_t *response = (core_itcp_response_t *)userdata;

    if (itcp_handle->recv_handler == NULL) {
        return;
    }

    switch (packet->type) {
        case AIOT_ITCPRECV_STATUS_CODE: {
            itcp_handle->recv_handler(itcp_handle, packet, itcp_handle->userdata);
        }
        break;
        case AIOT_ITCPRECV_HEADER:
        break;
        case AIOT_ITCPRECV_BODY: {
            uint8_t *content = itcp_handle->sysdep->core_sysdep_malloc(response->content_len + packet->data.body.len, CORE_ITCP_MODULE_NAME);
            if (content == NULL) {
                return;
            }
            memset(content, 0x00, response->content_len + packet->data.body.len);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                itcp_handle->sysdep->core_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {
        }
        break;
    }
}

static int32_t _core_itcp_send_auth(core_itcp_handle_t *itcp_handle, uint8_t *payload, uint32_t payload_len)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_request_t request;

    memset(&request, 0, sizeof(core_itcp_request_t));
    request.method = NULL;
    request.path = NULL;
    request.header = NULL;
    request.content = (uint8_t *)payload;
    request.content_len = payload_len;

    res = core_itcp_send(itcp_handle, &request);

    return res;
}

static int32_t _core_itcp_recv_auth(core_itcp_handle_t *itcp_handle, core_itcp_response_t *response)
{
    int32_t res = STATE_SUCCESS;
    uint64_t timenow_ms = 0;

    timenow_ms = itcp_handle->sysdep->core_sysdep_time();
    while (1) {
        if (timenow_ms >= itcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = itcp_handle->sysdep->core_sysdep_time();
        }
        if (itcp_handle->sysdep->core_sysdep_time() - timenow_ms >= itcp_handle->auth_timeout_ms) {
            break;
        }

        itcp_handle->recv_timeout_ms = itcp_handle->auth_timeout_ms;

        res = core_itcp_recv(itcp_handle);
        if (res < STATE_SUCCESS) {
            break;
        }
    }

    if (res < STATE_SUCCESS) {
        if (res != STATE_ITCP_READ_BODY_FINISHED) {
            return res;
        } else {
            res = STATE_SUCCESS;
        }
    } else {
        return STATE_ITCP_AUTH_NOT_FINISHED;
    }

    if (response->code != 0x00) {
        return STATE_ITCP_AUTH_CODE_FAILED;
    }

    return STATE_SUCCESS;
}

void *aiot_itcp_init(void)
{
    core_itcp_handle_t *itcp_handle = NULL;

    itcp_handle = core_itcp_init();
    if (itcp_handle == NULL) {
        return NULL;
    }

    itcp_handle->auth_timeout_ms = CORE_ITCP_DEFAULT_AUTH_TIMEOUT_MS;
    itcp_handle->long_connection = 1;

    itcp_handle->exec_enabled = 1;

    return itcp_handle;
}

int32_t aiot_itcp_setopt(void *handle, aiot_itcp_option_t option, void *data)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL || data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (option >= AIOT_ITCPOPT_MAX) {
        return STATE_USER_INPUT_OUT_RANGE;
    }

    if (itcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_itcp_exec_inc(itcp_handle);

    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    switch (option) {
        case AIOT_ITCPOPT_ETH:
        case AIOT_ITCPOPT_HOST:
        case AIOT_ITCPOPT_PORT:
        case AIOT_ITCPOPT_NETWORK_CRED:
        case AIOT_ITCPOPT_CONNECT_TIMEOUT_MS:
        case AIOT_ITCPOPT_SEND_TIMEOUT_MS:
        case AIOT_ITCPOPT_RECV_TIMEOUT_MS:
        case AIOT_ITCPOPT_DEINIT_TIMEOUT_MS:
        case AIOT_ITCPOPT_HEADER_BUFFER_LEN:
        case AIOT_ITCPOPT_BODY_BUFFER_LEN:
        case AIOT_ITCPOPT_EVENT_HANDLER: {
            itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);
            res = core_itcp_setopt(handle, (core_itcp_option_t)option, data);
            itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
        }
        break;
        case AIOT_ITCPOPT_USERDATA: {
            itcp_handle->userdata = data;
        }
        break;
        case AIOT_ITCPOPT_RECV_HANDLER: {
            itcp_handle->recv_handler = (aiot_itcp_recv_handler_t)data;
        }
        break;
        case AIOT_ITCPOPT_EXTEND_DEVINFO: {
            res = core_strdup(itcp_handle->sysdep, &itcp_handle->extend_devinfo, (char *)data, CORE_ITCP_MODULE_NAME);
        }
        break;
        case AIOT_ITCPOPT_AUTH_TIMEOUT_MS: {
            itcp_handle->auth_timeout_ms = *(uint32_t *)data;
        }
        break;
        case AIOT_ITCPOPT_LONG_CONNECTION: {
            itcp_handle->long_connection = *(uint8_t *)data;
        }
        break;
        default: {
            res = STATE_USER_INPUT_UNKNOWN_OPTION;
        }
        break;
    }
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);

    _core_aiot_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t aiot_itcp_auth(void *handle, uint8_t *payload, uint32_t payload_len)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_response_t response;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (itcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_itcp_exec_inc(itcp_handle);

    memset(&response, 0, sizeof(core_itcp_response_t));

    if ((res = core_itcp_setopt(itcp_handle, CORE_ITCPOPT_RECV_HANDLER, (void *)_core_itcp_auth_recv_handler)) < STATE_SUCCESS ||
        (res = core_itcp_setopt(itcp_handle, CORE_ITCPOPT_USERDATA, (void *)&response)) < STATE_SUCCESS) {
        return res;
    }
    if (itcp_handle->network_handle == NULL ||
        (itcp_handle->network_handle != NULL && itcp_handle->long_connection == 0)) {
        if ((res = core_itcp_connect(itcp_handle)) < STATE_SUCCESS) {
            _core_aiot_itcp_exec_dec(itcp_handle);
            return res;
        }
    }

    /* send auth request */
    res = _core_itcp_send_auth(itcp_handle, payload, payload_len);
    if (res < STATE_SUCCESS) {
        _core_aiot_itcp_exec_dec(itcp_handle);
        return res;
    }

    /* recv auth response */
    res = _core_itcp_recv_auth(itcp_handle, &response);

    if (response.content != NULL) {
        itcp_handle->sysdep->core_sysdep_free(response.content);
    }

    _core_aiot_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t aiot_itcp_send(void *handle, char *topic, uint8_t *payload, uint32_t payload_len)
{
    (void)topic;

    int32_t res = STATE_SUCCESS;
    core_itcp_request_t request;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL || payload == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }
    if (payload_len == 0) {
        return STATE_USER_INPUT_OUT_RANGE;
    }
    if (itcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_itcp_exec_inc(itcp_handle);

    if (itcp_handle->network_handle == NULL ||
        (itcp_handle->network_handle != NULL && itcp_handle->long_connection == 0)) {
        _core_aiot_itcp_exec_dec(itcp_handle);
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    memset(&request, 0, sizeof(core_itcp_request_t));
    request.method = NULL;
    request.path = NULL;
    request.header = NULL;
    request.content = (uint8_t *)payload;
    request.content_len = payload_len;

    res = core_itcp_send(itcp_handle, &request);

    _core_aiot_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t aiot_itcp_recv(void *handle)
{
    int32_t res = STATE_SUCCESS;
    uint64_t timenow_ms = 0;
    core_itcp_response_t response;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }
    if (itcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }
    if (itcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_itcp_exec_inc(itcp_handle);

    memset(&response, 0, sizeof(core_itcp_response_t));
    if ((res = core_itcp_setopt(itcp_handle, CORE_ITCPOPT_RECV_HANDLER, (void *)_core_itcp_recv_handler)) < STATE_SUCCESS ||
        (res = core_itcp_setopt(itcp_handle, CORE_ITCPOPT_USERDATA, (void *)&response)) < STATE_SUCCESS) {
        return res;
    }

    itcp_handle->recv_timeout_ms = CORE_ITCP_DEFAULT_RECV_TIMEOUT_MS;

    timenow_ms = itcp_handle->sysdep->core_sysdep_time();
    while (1) {
        if (timenow_ms >= itcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = itcp_handle->sysdep->core_sysdep_time();
        }
        if (itcp_handle->sysdep->core_sysdep_time() - timenow_ms >= itcp_handle->recv_timeout_ms) {
            break;
        }

        res = core_itcp_recv(itcp_handle);
        if (res < STATE_SUCCESS) {
            break;
        }
    }

    if (res < STATE_SUCCESS) {
        if (res != STATE_ITCP_READ_BODY_FINISHED && res != STATE_ITCP_HEADER_BUFFER_TOO_SHORT) {
            _core_aiot_itcp_exec_dec(itcp_handle);
            if (response.content != NULL) {
                itcp_handle->sysdep->core_sysdep_free(response.content);
            }
            return res;
        } else {
            res = STATE_SUCCESS;
        }
    } else {
        if (response.content != NULL) {
            itcp_handle->sysdep->core_sysdep_free(response.content);
        }
        return STATE_ITCP_RECV_NOT_FINISHED;
    }

    if (itcp_handle->recv_handler != NULL) {
        aiot_itcp_recv_t packet;

        packet.type = AIOT_ITCPRECV_BODY;
        packet.data.body.buffer = response.content;
        packet.data.body.len = response.content_len;

        itcp_handle->recv_handler(itcp_handle, &packet, itcp_handle->userdata);
    }

    if (response.content != NULL) {
        itcp_handle->sysdep->core_sysdep_free(response.content);
    }

    _core_aiot_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t aiot_itcp_deinit(void **p_handle)
{
    uint64_t deinit_timestart = 0;
    core_itcp_handle_t *itcp_handle = NULL;

    if (p_handle == NULL || *p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    itcp_handle = *(core_itcp_handle_t **)p_handle;

    if (itcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    itcp_handle->exec_enabled = 0;
    deinit_timestart = itcp_handle->sysdep->core_sysdep_time();
    do {
        if (itcp_handle->exec_count == 0) {
            break;
        }
        itcp_handle->sysdep->core_sysdep_sleep(CORE_ITCP_DEINIT_INTERVAL_MS);
    } while ((itcp_handle->sysdep->core_sysdep_time() - deinit_timestart) < itcp_handle->deinit_timeout_ms);

    if (itcp_handle->exec_count != 0) {
        return STATE_ITCP_DEINIT_TIMEOUT;
    }

    if (itcp_handle->extend_devinfo != NULL) {
        itcp_handle->sysdep->core_sysdep_free(itcp_handle->extend_devinfo);
    }
    if (itcp_handle->token != NULL) {
        itcp_handle->sysdep->core_sysdep_free(itcp_handle->token);
    }

    core_itcp_deinit(p_handle);

    return STATE_SUCCESS;
}
